home *** CD-ROM | disk | FTP | other *** search
- ╓───────────────╖
- ║ Week 1, Day 1 ║
- ╙───────────────╜
-
- Today you will learn how to make a PACMAN symbol move across the screen.
-
- Start QBasic and type in the following program:
-
- CLS
- PRINT "Below is PACMAN himself!"
- PRINT "C"
-
- Now RUN the program by pressing ALT-R and then S.
- Is the graphics a little unconvincing? Well, we have to start somewhere!
-
- The command CLS clears the screen and we'll use it often.
-
- Lets put PACMAN in the middle of the screen.
- The command you need is LOCATE.
- Type (or edit) and RUN the following:
-
- CLS
- LOCATE 12,40
- PRINT "C"
-
- Easy as pie!
- The first value between the brackets indicates the row, ranging from 1 at the
- top to 23 at the bottom on the screen.
- The second value is the column and ranges from 1 at the left to 80 at the right
- on the screen.
-
- Now to make PACMAN move!
- You could do it this way:
-
- LOCATE 12,1
- PRINT "C"
- LOCATE 12,1
- PRINT " " :REM delete the old picture
- LOCATE 12,2
- PRINT "C" :REM draw a new picture to the right
-
- (REM statements are ignored by the computer and you dont have to type )
- (them in. )
- (I will use REM statements to clarify and comment upon my programming.)
-
- This method will take forever to program so lets start using variables.
-
- A variable is described has a name and contains a value.
- This illustrates the difference between a numeric- and a string variable:
-
- contents ───-> │ 10 │ │ "twenty" │
- └────┘ └──────────┘
- name ───-> apples pears$
- (numeric) (string)
-
- The "$" in "pears$" is pronounced "string" and identifies it as a string
- variable.
- When you PRINT a variable its contents is shown, not its name.
- Type (or edit) and RUN:
-
- CLS
- apples = 10
- pears$ = "twenty"
- PRINT "Amount of apples are "; apples
- PRINT "Amount of pears are "; pears$
-
- Notice how the text between the double quotes is displayed literally while
- the variable name is replaced by its contents.
- The "=" operator assigns a value to a variable and does it by working from
- right to left, e.g. in "apples = 10" the "10" goes into "apples".
- The ";" causes the computer to remember where it last PRINTed and the following
- PRINT will continue at that position.
- PRINT "ABC" will give the same output as PRINT "A"; "B"; "C"
-
- While PACMAN is warming up for his jog, lets make things even easier on
- ourselves.
- You might have figured that I want to use variables in the following way:
-
- column = 1
- LOCATE 12,column :REM This translates to LOCATE 12,1
- PRINT "C"
- column = column + 1 :REM Lets calculate the value:
- :REM column + 1 gives you 1 + 1, in other words 2
- LOCATE 12,column :REM This translates to LOCATE 12,2
- PRINT "C"
-
- Still lots of repetition!
- An easier way is to make the computer go in a "loop", increasing the value of
- "column" and printing PACMAN.
- Type (or edit) and RUN:
-
- CLS
- FOR column = 1 TO 80
- LOCATE 12,column
- PRINT "C"
- NEXT column
-
- Hope I haven't put you off programming altogether with that one!
- You've implemented as so-called "FOR..NEXT loop" and here's the low-down on it:
- The "FOR" line tells the computer that a variable called column will start off
- at 1 and then increase in value until it reaches 80.
- The "NEXT" line increases column's value with one and makes the program jump
- back to the "LOCATE" line.
- Everything between the "FOR" and the "NEXT" is thus the actual loop and gets
- repeated 80 times.
-
- If you still dont understand the concept of the "FOR..NEXT loop" then type and
- RUN:
-
- CLS
- FOR values = 1 TO 10
- PRINT values
- NEXT values
-
- Hope you've found the experience enlightening!
-
- Clearly also is that everything is happening too fast.
- PACMAN completes his trip in the blink of an eye or, depending on how fast
- your computer is, even less!
- The solution is to use the SLEEP command which waits a number of seconds.
- Type (or edit) and RUN:
-
- CLS
- FOR column = 1 TO 80
- LOCATE 12,column
- PRINT "C"
- SLEEP 1
- NEXT column
-
- Too slow now!
- Another way is to use an empty FOR..NEXT loop that does nothing but kill time.
- That way you can change the value of the loop untill it provides an acceptable
- delay for your computer.
-
- Another problem was that the PACMAN isn't deleted in its old position and you
- are left with a whole row of "CCCCCC"s. We'll solve that now.
- Here's the final code for the PACMAN 100m sprint:
- Type (or edit) and RUN!
-
- CLS
- FOR column = 1 TO 79
- LOCATE 12,column
- PRINT " C"
- FOR nothing = 1 TO 100 :REM <─┬─ This just delays the computer
- NEXT nothing :REM ─┘
- NEXT column
-
- If you didnt see a thing the program is probably still too fast for your
- computer, so try changing the limit of the "nothing" loop to 300 or more.
-
- The space before PACMAN cleverly deletes its old position.
- How? Two characters are displayed (a " " and a "C") but the position only
- shift one to the right.
- When the two characters are displayed at their new position, the " " overlaps
- with the "C" previously displayed and effectively deletes it.
- That's all for today!
-
- -------------------------------------------------------------------------------
- ╓───────────────╖
- ║ Week 1, Day 2 ║
- ╙───────────────╜
-
- Watching PACMAN move is fun, but being in control opens up endless
- possibilities..
- Enough said!
- Remember the FOR..NEXT loop?
- Lets have a look at a different kind of loop.
- Type (or edit) and RUN:
-
- CLS
- value = 1
- DO WHILE value < 11
- PRINT value :REM <─┬─ This is the loop
- value = value + 1 :REM ─┘
- LOOP
-
- This displayed numbers 1 through 10.
- The program loops while "value" is less than 11.
- We will now use the DO..WHILE loop to read the keyboard and display the
- characters that you press.
- However, there's one snag. The program will loop "forever" unless you stop it!
- Pressing CTL and BREAK at the same time will do this.
- Type (or edit) and RUN:
-
- CLS
- DO :REM there's no WHILE, so DO forever..
- keyed$ = INKEY$ :REM make keyed$ = the key pressed
- IF keyed$ <> "" THEN PRINT keyed$ :REM if keyed$ isnt empty then display it
- LOOP
-
- I bet INKEY$ has you confused!
- INKEY$ is a special string variable.
- The computer always sets the value of INKEY$ to the key that you press.
- While you're not pressing any keys it will contain nothing (nothing is "").
- The IF command is very straight forward.
- IF something is true THEN do something.. got it?
- So IF keyed$ is not equal to nothing THEN its value gets PRINTed.
- If I didnt include the IF clause then keyed$ would always be PRINTed, whether
- it contained a value or not.
- Try leaving out the IF..THEN part and you'll see what I mean!
- Lots and lots of "nothings" fill the screen!
-
- Armed with our new commands we can finally control PACMAN with the keyboard.
- Type (or edit) and RUN:
-
- CLS
- row = 12
- column = 40
- DO
- DO :REM <─┐ this loop waits for a key to be pressed
- keyed$ = INKEY$ :REM │
- LOOP UNTIL keyed$ <> "" :REM ─┘
- LOCATE row, column
- PRINT " " :REM this erases the "C"
- IF keyed$ = "q" THEN row = row - 1
- IF keyed$ = "a" THEN row = row + 1
- IF keyed$ = "o" THEN column = column - 1
- IF keyed$ = "p" THEN column = column + 1
- LOCATE row, column
- PRINT "C" :REM shows the "C" at the new position
- LOOP
-
- Note that you can use your own choice of keys by replacing "q","a","o" and "p".
- Using special keys like the cursor keys is another cup of tea.
-
- If you move PACMAN outside the screen boundaries you'll get an error message.
- To prevent this you must add checks to the program.
- Just replace the IFs with the following lines and RUN:
-
- IF (keyed$ = "q") AND (row > 1) THEN row = row - 1
- IF (keyed$ = "a") AND (row < 23) THEN row = row + 1
- IF (keyed$ = "o") AND (column > 1) THEN column = column - 1
- IF (keyed$ = "p") AND (column < 80) THEN column = column + 1
-
- This should do for today.
- Hope you enjoyed this as much as I did!
-
- -------------------------------------------------------------------------------
- ╓───────────────╖
- ║ Week 1, Day 3 ║
- ╙───────────────╜
-
- A playing area is essential to any game so lets design a maze for our PACMAN.
-
- First I'll have to tell you about arrays.
- Take a look at this example:
-
- DIM values(3)
- values(1) = 5
- values(2) = 92
- values(3) = 45
-
- Arrays are variables containing multiple elements.
- Each element is named after the array followed by a number to specify the
- position of the element.
- Each element has its own value.
- Type (or edit) and RUN the following to see how arrays can save you from a lot
- of repetitive programming:
-
- CLS
- DIM values(10)
- FOR count = 1 TO 10
- values(count) = count
- NEXT count
- FOR count = 1 to 10
- PRINT values(count)
- NEXT count
-
- Remember that "count" ranges from 1 to 10 so the line "values(count) = count"
- translates to "values(1) = 1, values(2) = 2, values(3) = 3" etc.
-
- Now lets define our maze. Type (or edit) and RUN:
-
- CLS
- DIM maze$(6)
- maze$(1) = "#########"
- maze$(2) = "# # #"
- maze$(3) = "# # # # #"
- maze$(4) = "# # # #"
- maze$(5) = "# # #"
- maze$(6) = "#########"
- FOR count = 1 to 6
- PRINT maze$(count)
- NEXT count
-
- Now lets put PACMAN in the maze.
- Wait - there's nothing that will keep him from moving over the walls.
- To solve that problem I'll show you how to inspect the maze (the contents of
- maze$).
-
- The command MID$ allows you to look at parts of a string variable.
- It has the following format: MID$(name of string, starting position, number of
- letters).
- Lets look at an example:
- alphabet$ = "ABCDE"
- PRINT MID$(alphabet$, 1, 2) :REM displays "AB"
- PRINT MID$(alphabet$, 3, 1) :REM displays "C"
-
- Every time PACMAN moves we'll use his position (row and column) to see if he
- overlaps a wall in the maze.
- Type (or edit) and RUN: (you'll have to press CTRL-BREAK to stop the program)
-
- CLS
- DIM maze$(6)
- maze$(1) = "#########"
- maze$(2) = "# # #"
- maze$(3) = "# # # # #"
- maze$(4) = "# # # #"
- maze$(5) = "# # #"
- maze$(6) = "#########"
- row = 5
- column = 3
- DO
- LOCATE 1, 1
- FOR count = 1 to 6
- PRINT maze$(count) :REM PRINT the maze
- NEXT count
- LOCATE row, column
- PRINT "C" :REM PRINT PACMAN
- DO
- keyed$ = INKEY$
- LOOP UNTIL keyed$ <> ""
- oldRow = row :REM remember old position of PACMAN
- oldColumn = column
- IF keyed$ = "q" THEN row = row - 1
- IF keyed$ = "a" THEN row = row + 1
- IF keyed$ = "o" THEN column = column - 1
- IF keyed$ = "p" THEN column = column + 1
- IF MID$(maze$(row), column, 1) = "#" THEN
- row = oldRow :REM <─┬ move PACMAN back to his
- column = oldColumn :REM ─┘ old position
- END IF
- LOOP
-
- All that needs mentioning is that I used the "IF..END IF" structure to allow
- for more than one action.
-
- If you type in the examples you can save yourself some time by saving the
- PACMAN game to disk (press ALT-F then S), because we will re-use the game
- during the rest of the week.
-
- Tomorrow we'll add dots and a ghost to the maze!
-
- -------------------------------------------------------------------------------
- ╓───────────────╖
- ║ Week 1, Day 4 ║
- ╙───────────────╜
-
- Add the dots to the maze by changing the following lines:
-
- maze$(1) = "#########"
- maze$(2) = "#...#...#"
- maze$(3) = "#.#.#.#.#"
- maze$(4) = "#.#...#.#"
- maze$(5) = "#...#...#"
- maze$(6) = "#########"
-
- As you might have guessed we'll inspect the maze each time PACMAN moves and
- keep count of the number of dots he eat.
- A dot has to be removed when eaten.
- When our count reaches 21 (count them!) dots, the game ends.
-
- MID$ can be used to change a string variable as well as inspecting it.
- Look at the following example:
-
- alphabet$="ABCDE"
- MID$(alphabet$, 2, 3) = "XYZ"
- PRINT alphabet$ :REM displays "AXYZE"
-
- We'll use MID$ to replace the dots with blanks when PACMAN eats them.
- Type (or edit) and RUN: (changes in the program are marked)
-
- CLS
- DIM maze$(6)
- maze$(1) = "#########"
- maze$(2) = "#...#...#"
- maze$(3) = "#.#.#.#.#"
- maze$(4) = "#.#...#.#"
- maze$(5) = "#...#...#"
- maze$(6) = "#########"
- row = 5
- column = 3
- dots = 21 :REM <──- new
- DO
- LOCATE 1, 1
- FOR count = 1 to 6
- PRINT maze$(count)
- NEXT count
- LOCATE row, column
- PRINT "C"
- :REM <─┬─ new
- IF dots = 0 THEN
- LOCATE 8, 1 :REM │
- PRINT "You have won!" :REM │
- END :REM │
- END IF :REM ─┘
- DO
- keyed$ = INKEY$
- LOOP UNTIL keyed$ <> ""
- oldRow = row
- oldColumn = column
- IF keyed$ = "q" THEN row = row - 1
- IF keyed$ = "a" THEN row = row + 1
- IF keyed$ = "o" THEN column = column - 1
- IF keyed$ = "p" THEN column = column + 1
- IF MID$(maze$(row), column, 1) = "#" THEN
- row = oldRow
- column = oldColumn
- END IF
- :REM <─┬─ new
- IF MID$(maze$(row), column, 1) = "." THEN
- MID$(maze$(row), column, 1) = " " :REM │
- dots = dots - 1 :REM │
- END IF :REM ─┘
- LOOP
-
- (QBasic does not allow me to place a ":REM" next to a IF..THEN line. )
- (So if you suspect a REM is missing, see if its next tot an IF..THEN.)
-
- The reason why we dont do the "IF dots = 0" test directly after inspecting the
- maze is because we want to draw PACMAN in his new position before ending the
- game.
-
- Here is a better (or clearer) way of inspecting keyed$ and maze$:
-
- REM examine the keys
- SELECT CASE keyed$
- CASE IS = "q"
- row = row - 1
- CASE IS = "a"
- row = row + 1
- CASE IS = "o"
- column = column - 1
- CASE IS = "p"
- column = column + 1
- END SELECT
-
- REM examine the maze
- SELECT CASE MID$(maze$(row), column, 1)
- CASE IS = "#"
- row = oldRow
- column = oldColumn
- CASE IS = "."
- MID$(maze$(row), column, 1) = " "
- dots = dots - 1
- END SELECT
-
- SELECT makes it a lot easier to read the program.
- Also notice the way I put spaces before (indent) some words.
- Another good idea is to use blank lines to seperate the various parts of your
- program.
-
- Lets place a ghost in the maze.
- We'll just display the ghost for now and detect whether PACMAN collides with
- it. Tomorrow we can make it move.
-
- Detecting whether there's a collision is a simple matter of comparing PACMAN's
- and the ghost's positions (row and column).
- Type (or edit) and RUN:
-
- CLS
-
- DIM maze$(6)
- maze$(1) = "#########"
- maze$(2) = "#...#...#"
- maze$(3) = "#.#.#.#.#"
- maze$(4) = "#.#...#.#"
- maze$(5) = "#...#...#"
- maze$(6) = "#########"
-
- row = 5
- column = 3
- ghostRow = 2 :REM <─┬─ new
- ghostColumn = 7 :REM ─┘
- dots = 21
-
- DO
-
- LOCATE 1, 1
- FOR count = 1 to 6
- PRINT maze$(count)
- NEXT count
-
- LOCATE row, column
- PRINT "C"
-
- LOCATE ghostRow, ghostColumn :REM <─┬─ new
- PRINT "G" :REM ─┘
-
- IF dots = 0 THEN
- LOCATE 8, 1
- PRINT "You have won!"
- END
- END IF
- :REM <─┬─ new
- IF ((row = ghostRow) AND (column = ghostColumn)) THEN
- LOCATE 8, 1 :REM │
- PRINT "You've been caught!" :REM │
- END :REM │
- END IF :REM ─┘
-
- DO
- keyed$ = INKEY$
- LOOP UNTIL keyed$ <> ""
-
- oldRow = row
- oldColumn = column
-
- REM examine the keys
- SELECT CASE keyed$
- CASE IS = "q"
- row = row - 1
- CASE IS = "a"
- row = row + 1
- CASE IS = "o"
- column = column - 1
- CASE IS = "p"
- column = column + 1
- END SELECT
-
- REM examine the maze
- SELECT CASE MID$(maze$(row), column, 1)
- CASE IS = "#"
- row = oldRow
- column = oldColumn
- CASE IS = "."
- MID$(maze$(row), column, 1) = " "
- dots = dots - 1
- END SELECT
-
- LOOP
-
- Phew! I need a rest after that!
- Remember to save the program for tomorrow.
-
- -------------------------------------------------------------------------------
- ╓───────────────╖
- ║ Week 1, Day 5 ║
- ╙───────────────╜
-
- Lets give the ghost some "artificial intelligence".
-
- As you might have suspected we will compare his positions with PACMAN's and
- move him one position nearer.
- Allowing the ghost to move diagonally will give it an unfair advantage so
- ghostRow and ghostColumn will not change at the same time.
- Of course the ghost will also have to be blocked by the walls of the maze.
- We dont have to check for dots because the ghost dont eat them.
-
- So what are we waiting for? Type (or edit) and RUN!
-
- CLS
-
- DIM maze$(6)
- maze$(1) = "#########"
- maze$(2) = "#...#...#"
- maze$(3) = "#.#.#.#.#"
- maze$(4) = "#.#...#.#"
- maze$(5) = "#...#...#"
- maze$(6) = "#########"
-
- row = 5
- column = 3
- ghostRow = 2
- ghostColumn = 7
- dots = 21
-
- DO
-
- LOCATE 1, 1
- FOR count = 1 to 6
- PRINT maze$(count)
- NEXT count
-
- LOCATE row, column
- PRINT "C"
-
- LOCATE ghostRow, ghostColumn
- PRINT "G"
-
- IF dots = 0 THEN
- LOCATE 8, 1
- PRINT "You have won!"
- END
- END IF
-
- IF ((row = ghostRow) AND (column = ghostColumn)) THEN
- LOCATE 8, 1
- PRINT "You've been caught!"
- END
- END IF
-
- DO
- keyed$ = INKEY$
- LOOP UNTIL keyed$ <> ""
-
- oldRow = row
- oldColumn = column
-
- REM examine the keys
- SELECT CASE keyed$
- CASE IS = "q"
- row = row - 1
- CASE IS = "a"
- row = row + 1
- CASE IS = "o"
- column = column - 1
- CASE IS = "p"
- column = column + 1
- END SELECT
-
- REM examine the maze
- SELECT CASE MID$(maze$(row), column, 1)
- CASE IS = "#"
- row = oldRow
- column = oldColumn
- CASE IS = "."
- MID$(maze$(row), column, 1) = " "
- dots = dots - 1
- END SELECT
-
- REM move ghost closer to PACMAN :REM <─┐ everything from this point
- :REM │ onwards is new
- oldRow = ghostRow
- SELECT CASE ghostRow
- CASE < row
- ghostRow = ghostRow + 1
- CASE > row
- ghostRow = ghostRow - 1
- END SELECT
- IF MID$(maze$(ghostRow), ghostColumn, 1) = "#" THEN ghostRow = oldRow
-
- IF ghostRow = oldRow THEN
- oldColumn = ghostColumn
- SELECT CASE ghostColumn
- CASE < column
- ghostColumn = ghostColumn + 1
- CASE > column
- ghostColumn = ghostColumn - 1
- END SELECT
- IF MID$(maze$(ghostRow), ghostColumn, 1) = "#" THEN ghostColumn = oldColumn
- END IF
- LOOP
-
- Notice that I test whether ghostRow has stayed the same ( = oldRow) before
- testing for ghostColumn.
- This prevents diagonal movement for the ghost.
- Another problem is that the ghost waits for you to make a move before moving
- himself.
- The loop that waits for a key to be pressed is the culprit.
- Now we will have to put a delay somewhere in the program because the ghost will
- move at blinding speed!
- Replace the following lines in the program and RUN it:
-
- DO
- keyed$ = INKEY$
- LOOP UNTIL keyed$ <> ""
-
- with:
-
- keyed$ = INKEY$
- REM: kill time
- FOR nothing = 1 TO 500
- NEXT nothing
-
- If the game is still too fast then change the limit of "nothing" to 1000 or
- more.
-
- The game is still too difficult because the ghost is too intelligent - it
- follows you at every possible opportunity.
- To add some randomness to its pattern we will use the command RND.
- The value of RND is never the same (for all practical purposes) but is always
- a value between 0 and 1.
- The condition "IF RND < 0.1 " is true roughly 10% of the time.
- Here is the entire listing with the changes added: (Type (or edit) and RUN)
-
- CLS
-
- DIM maze$(6)
- maze$(1) = "#########"
- maze$(2) = "#...#...#"
- maze$(3) = "#.#.#.#.#"
- maze$(4) = "#.#...#.#"
- maze$(5) = "#...#...#"
- maze$(6) = "#########"
-
- row = 5
- column = 3
- ghostRow = 2
- ghostColumn = 7
- dots = 21
-
- DO
-
- LOCATE 1, 1
- FOR count = 1 to 6
- PRINT maze$(count)
- NEXT count
-
- LOCATE row, column
- PRINT "C"
-
- LOCATE ghostRow, ghostColumn
- PRINT "G"
-
- IF dots = 0 THEN
- LOCATE 8, 1
- PRINT "You have won!"
- END
- END IF
-
- IF ((row = ghostRow) AND (column = ghostColumn)) THEN
- LOCATE 8, 1
- PRINT "You've been caught!"
- END
- END IF
-
- keyed$ = INKEY$
-
- FOR nothing = 1 TO 500 :REM change if your computer is faster
- NEXT nothing
-
- oldRow = row
- oldColumn = column
-
- REM examine the keys
- SELECT CASE keyed$
- CASE IS = "q"
- row = row - 1
- CASE IS = "a"
- row = row + 1
- CASE IS = "o"
- column = column - 1
- CASE IS = "p"
- column = column + 1
- END SELECT
-
- REM examine the maze
- SELECT CASE MID$(maze$(row), column, 1)
- CASE IS = "#"
- row = oldRow
- column = oldColumn
- CASE IS = "."
- MID$(maze$(row), column, 1) = " "
- dots = dots - 1
- END SELECT
- :REM <─┬─ new
- IF RND < 0.1 THEN
- :REM ─┘
- REM move ghost closer to PACMAN
- oldRow = ghostRow
- SELECT CASE ghostRow
- CASE < row
- ghostRow = ghostRow + 1
- CASE > row
- ghostRow = ghostRow - 1
- END SELECT
- IF MID$(maze$(ghostRow), ghostColumn, 1) = "#" THEN ghostRow = oldRow
-
- IF ghostRow = oldRow THEN
- oldColumn = ghostColumn
- SELECT CASE ghostColumn
- CASE < column
- ghostColumn = ghostColumn + 1
- CASE > column
- ghostColumn = ghostColumn - 1
- END SELECT
- IF MID$(maze$(ghostRow), ghostColumn, 1) = "#" THEN ghostColumn = oldColumn
- END IF
- END IF :REM <─── new
- LOOP
-
- Another way to slow down the game is to use a variable "wait" and add 1 to it
- every time the program loops.
- Only when "wait" = 20 the ghost is allowed to move.
- Remember to reset "wait" to 0 after moving, or its value will be 21, 22, etc.
- and never again 20!
- If you use this method you must remove the FOR..NEXT loop that kills time.
-
- Next week we'll do an RPG!
-
- -------------------------------------------------------------------------------
-